<?php
/**
 * コンテナ抽象クラス
 *
 * @author tanaka
 * @package Container
 * @subpackage Abstract
 */
abstract class Container_Abstract implements IteratorAggregate, ArrayAccess
{
	/**
	 * @var ReflectionClass
	 */
	private $_reflection;
	
	/**
	 * コンストラクタ
	 *
	 * @param array $params
	 * @return
	 */
	public function __construct(array $params = array())
	{
		$this->_reflection = new ReflectionClass($this);
		$this->setPropatys($params);
	}

	/**
	 * プロパティをセットします。
	 *
	 * @param array $params
	 * @return Container_Abstract
	 */
	public function setPropatys(array $params)
	{
		foreach ($params as $key => $value) {
			try {
				if($this->_reflection->hasProperty($key)){
					$this->$key = $value;
				}
			}
			catch (Exception $e) {
				ASSERT('propaty:' . $key . ' is not Exist!!');
			}
		}
		
		return $this;
	}
	
	/**
	 * 指定されたプロパティをデフォルトにリセットします。
	 * 
	 * @param string|array $keys
	 * @return void
	 */
	public function resetParts($keys)
	{
		if(is_array($keys)){
			foreach($keys as $key){
				$this->resetParts($key);
			}
			return;
		}
		
		$reflection = new ReflectionClass($this);
		$default = $reflection->getDefaultProperties();
		
		if(!isset($default[$keys])){
			$this->$keys = null;
		}
		else{
			$this->$keys = $default[$keys];
		}
		
		unset($reflection);
		
		return;
	}

	/**
	 * ArrayObjectに変換します。
	 *
	 * @return ArrayObject
	 */
	public function getIterator()
	{
		return new ArrayObject($this);
	}

	/**
	 * 要素があるか返します
	 * @param string $offset
	 * @return bool
	 */
	public function offsetExists($offset)
	{
		return isset($this->$offset);
	}

	/**
	 * オフセットに値を入れます
	 *
	 * @param string $offset
	 * @param mixed $value
	 * @return void
	 */
	public function offsetSet($offset, $value)
	{
		$this->_checkAccessablePropaty($offset);

		$this->$offset = $value;
	}

	/**
	 * オフセットを取得します。
	 * @param string $offset
	 * @return mixed
	 */
	public function offsetGet($offset)
	{
		$this->_checkAccessablePropaty($offset);

		return $this->$offset;
	}

	/**
	 * オフセットを初期化します。
	 *
	 * @param string $offset
	 * @return void
	 */
	public function offsetUnset($offset)
	{
		$this->_checkAccessablePropaty($offset);

		$this->$offset = null;
	}

	/**
	 * 配列化します
	 *
	 * @return array
	 */
	public function toArray()
	{
		return $this->getIterator()->getArrayCopy();
	}

	/**
	 * 存在しないプロパティにアクセス
	 *
	 * @param string $name
	 * @param string $value
	 * @throws Exception
	 * @return void
	 */
	public function __set($name, $value)
	{
		throw new Exception(sprintf('クラス【%s】に%sというプロパティはありません。', get_class($this), $name));
	}

	/**
	 * アクセスできるかチェックします。
	 *
	 * @param string $propaty
	 * @throws Exception
	 * @return void
	 */
	protected function _checkAccessablePropaty($propatyName)
	{
		if (strpos($propatyName, '*') !== false) {
			throw new Exception(sprintf('クラス【%s】の%sというプロパティはアクセス禁止です。', get_class($this), $propatyName));
		}
	}
	
	/**
	* 1項目でも入力されているか返す
	*
	* @return bool
	*/
	public function isInputed()
	{
		$ref = $this->_reflection;
	
		$defaults = $ref->getDefaultProperties();
	
		foreach($ref->getProperties(ReflectionProperty::IS_PUBLIC) as $prop){
			$name = $prop->getName();
				
			if($this->$name != $defaults[$name]){
				return true;
			}
		}
	
		return false;
	}
}
